<!-- 支持单选父子级、搜索的自定义穿梭框 -->
<template>
  <a-spin :spinning="getTreeLoading || getListLoading">
    <div class="main-box">
      <div class="main-box__item">
        <div class="item__header">
          <div :title="pageOptions.title">{{ pageOptions.title }}</div>
          <div @click="handleAdd">
            <i class="iconfont icon-add-line"></i>
            新建
          </div>
        </div>
        <a-radio-group :value="value" class="item__body" @change="changeRadioGroup">
          <div class="body__search w-328">
            <a-form-item>
              <g-input
                v-model:value="searchValue"
                v-trim
                :placeholder="pageOptions.placeholder"
                :disabled="disabled"
                @keydown.enter.prevent
                @keyup.enter="onSearch"
              >
                <template #prefix> <i class="iconfont icon-search-line" @click="onSearch"></i> </template>
              </g-input>
            </a-form-item>
          </div>
          <div class="mt-8 tree-body my-scrollbar">
            <!-- 搜索列表 -->
            <recycle-scroller
              v-show="isOpenSearch"
              class="select-item-list"
              :items="searchList"
              :item-size="30"
              key-field="key"
              v-slot="{ item }"
            >
              <div class="pb-8">
                <div class="select-item">
                  <a-radio :value="item.key" @click="changeRadio">
                    <div class="select-item__name" :title="item.title">
                      <i :class="['iconfont', item.isLeaf ? 'icon-File' : 'icon-folder-3-line']"></i>
                      {{ item.title }}
                    </div>
                  </a-radio>
                </div>
              </div>
            </recycle-scroller>
            <a-breadcrumb
              v-if="crumbs.length > 1 && !isOpenSearch"
              ref="breadcrumbRef"
              class="crumbs"
              :class="[crumbs.length > 1 && 'exceed']"
            >
              <template #separator><span>></span></template>
              <template v-for="(crumb, index) in crumbs" :key="index">
                <a-breadcrumb-item v-if="crumb.overlay === undefined" style="cursor: pointer" @click="breadClick(crumb)">
                  <span :title="crumb.name" class="bread-text elliptical-1">{{ crumb.name }}</span>
                </a-breadcrumb-item>

                <a-breadcrumb-item v-else>
                  {{ crumb.name }}
                  <template #overlay>
                    <a-menu>
                      <a-menu-item v-for="(it, index) in crumb.overlay" :key="index" @click="breadClick(it)">
                        <span :title="it.name">{{ it.name }} </span>
                      </a-menu-item>
                    </a-menu>
                  </template>
                </a-breadcrumb-item>
              </template>
            </a-breadcrumb>
            <recycle-scroller
              v-show="!isOpenSearch"
              :class="[!isOpenSearch ? 'select-item-list' : 'select-tree-item-list', 'my-scrollbar']"
              :items="treeData"
              :item-size="30"
              key-field="key"
              v-slot="{ item }"
              :style="{ height: 300 - breadcrumbRef?.$el.offsetHeight + 'px' }"
            >
              <div>
                <div class="select-item">
                  <a-radio :value="item.key" @click="changeRadio">
                    <div :class="['select-item__name', disabled ? 'disabled-color' : '']" :title="item.title">
                      <i :class="['iconfont', item.isLeaf ? 'icon-File' : 'icon-folder-3-line', disabled ? 'disabled-color' : '']"></i>
                      {{ item.title }}
                    </div>
                  </a-radio>
                  <a-button v-if="pageOptions.canDown && !item.isLeaf" type="link" @click.stop="nextLevel(item)">下级</a-button>
                </div>
              </div>
            </recycle-scroller>
          </div>
        </a-radio-group>
      </div>
    </div>
    <add-form ref="addFormRef" v-model:show="showAddForm" :loading="okButtonLoading" @submit="onAddFormSubmit" />
  </a-spin>
</template>

<script lang="ts" setup>
  import { Form, message } from 'ant-design-vue'
  import { cloneDeep } from 'g6-fn'
  import { ref } from 'vue'
  import { useRequest } from 'vue-request'

  import { customerMyDateSetApi, dataCleaningApi, modelLibraryApi, radioTreeDataApi } from '@/services/api'

  import AddForm from './components/AddForm.vue'
  import { defaultProps } from './options'
  import { Props, TreeNode } from './types'

  type CrumbType = {
    key: string
    name: string
    overlay?: CrumbType[]
  }

  const props = withDefaults(defineProps<Props>(), defaultProps)

  const emits = defineEmits<{
    (e: 'update:value', value?: string | number): void
  }>()

  const typeOptions = {
    // 数据集
    data: {
      //获取树数据的api
      getTreeDataApi: dataCleaningApi.dataBucketTree,
      //获取列表数据的api
      getListDataApi: dataCleaningApi.searchBucketList,
      //第一层新增的api
      addApi: customerMyDateSetApi.add,
      //第二层新增的api
      addApi2: customerMyDateSetApi.createFile,
      fieldNames: {
        children: 'children',
        title: 'name',
        key: 'id'
      },
      placeholder: '请输入关键词',
      title: '导出到我的数据集',
      crumbsFirstTitle: '数据集',
      canDown: true
    },
    //模型库
    model: {
      getTreeDataApi: radioTreeDataApi.modelTree,
      getListDataApi: radioTreeDataApi.searchModel,
      //第一层新增的api
      addApi: modelLibraryApi.addMyModel,
      //第二层新增的api
      addApi2: customerMyDateSetApi.createFile,
      fieldNames: {
        children: 'children',
        title: 'name',
        key: 'id'
      },
      placeholder: '请输入关键词',
      title: '导出到我的模型',
      crumbsFirstTitle: '模型库',
      canDown: false
    }
  }

  const pageOptions = typeOptions[props.type]

  const formItemContext = Form.useInjectFormItemContext()

  /* 数据变动逻辑 */
  const checkedItems = ref<TreeNode[]>([]) // 选择的所有数据
  // 面包屑
  const crumbs = ref<CrumbType[]>([{ key: '', name: pageOptions.crumbsFirstTitle }])
  // 面包屑的缓存
  let crumbsCopy = ref<CrumbType[]>([{ key: '', name: pageOptions.crumbsFirstTitle }])
  // 隐藏的临界值
  const CRITICAL_VALUE = 4
  // 面包屑的ref
  const breadcrumbRef = ref()
  // 点击
  const breadClick = async (crumb: CrumbType) => {
    if (crumb.key === '') {
      clearBread()
    }
    await getTreeData(crumb.key)
    const index = crumbsCopy.value.findIndex(item => item.key === crumb.key) // 获取元素 a=3 的索引
    if (index !== -1) {
      crumbsCopy.value.splice(index + 1) // 截断数组
      if (crumbsCopy.value.length > CRITICAL_VALUE) {
        let arr = cloneDeep(crumbsCopy.value)
        const delList = arr.splice(2, arr.length - CRITICAL_VALUE)
        crumbs.value = arr
        crumbs.value.splice(2, 0, { name: '...', key: 'overlay', overlay: delList })
      } else {
        crumbs.value = cloneDeep(crumbsCopy.value)
      }
    }
  }

  /* 清除 */
  const clearBread = () => {
    // 面包屑
    crumbs.value = [{ key: '', name: '数据集' }]
    // 面包屑的缓存
    crumbsCopy.value = [{ key: '', name: '数据集' }]
  }

  /* 数据 */
  const treeData = ref<TreeNode[]>([])
  const { loading: getTreeLoading, runAsync: runGetTreeData } = useRequest<ResponseData<any[]>>(pageOptions.getTreeDataApi)

  /* 将数据渲染为树形节点 */
  const dataToTreeNode = (list: any[]): TreeNode[] => {
    return list.map((it: any) => ({
      title: it[pageOptions.fieldNames.title],
      key: it[pageOptions.fieldNames.key],
      children: it[pageOptions.fieldNames.children],
      isLeaf: it.type === 1 //0文件夹 1文件
    }))
  }

  /* 获取树形数据 */
  const getTreeData = async (id = '') => {
    isOpenSearch.value = false
    const res = await runGetTreeData({ id })
    treeData.value = dataToTreeNode(res.data)
  }

  /* 下一级 */
  const nextLevel = async (node: TreeNode) => {
    crumbsCopy.value.push({
      key: node.key,
      name: node.title
    })
    if (crumbsCopy.value.length > CRITICAL_VALUE) {
      let arr = cloneDeep(crumbsCopy.value)
      const delList = arr.splice(2, arr.length - CRITICAL_VALUE)
      crumbs.value = arr
      crumbs.value.splice(2, 0, { name: '...', key: 'overlay', overlay: delList })
    } else {
      crumbs.value.push({
        key: node.key,
        name: node.title
      })
    }

    await getTreeData(node.key)
  }

  /* 搜索模式逻辑 */
  const isOpenSearch = ref<boolean>(false)
  const searchValue = ref('')
  const searchList = ref<TreeNode[]>([])

  const { loading: getListLoading, runAsync: runGetListData } = useRequest<ResponseData<any>>(pageOptions.getListDataApi, {
    onSuccess: res => {
      searchList.value = dataToTreeNode(res.data)
    }
  })

  /* 搜索 */
  const onSearch = async () => {
    isOpenSearch.value = !!searchValue.value
    if (searchValue.value === '') {
      return
    }
    runGetListData({ name: searchValue.value })
  }

  /* 选择逻辑 */
  const changeRadioGroup = (e: any) => {
    const value = e.target.value
    emits('update:value', value)
    formItemContext.onFieldChange()
  }

  /* 单选 */
  const changeRadio = () => {
    // if (props.value === e.target.value) {
    //   emits('update:value', undefined)
    //   formItemContext.onFieldChange()
    // }
  }

  /* 新建 */
  const showAddForm = ref(false)
  const addFormRef = ref<InstanceType<typeof AddForm>>()
  const handleAdd = () => {
    showAddForm.value = true
  }

  const { loading: okButtonLoading, runAsync: runAddApi1 } = useRequest(pageOptions.addApi)
  const { runAsync: runAddApi2 } = useRequest(pageOptions.addApi2)
  const onAddFormSubmit = async (formState: any) => {
    try {
      if (crumbs.value.length > 1) {
        const bucketId = crumbs.value[1].key || 0
        const pid = crumbs.value.length === 2 ? 0 : crumbs.value[crumbs.value.length - 1].key || 0

        await runAddApi2({ name: formState.name, bucketId: bucketId, pid: pid }, { isAddUid: true })
      } else {
        await runAddApi1({ name: formState.name }, { isAddUid: true })
      }
      showAddForm.value = false
      getTreeData(crumbs.value[crumbs.value.length - 1].key)
      searchValue.value && runGetListData({ name: searchValue.value })
      message.success('新增成功')
      addFormRef.value?.resetForm()
    } catch (error) {}
  }

  const setSearchValue = (value = '') => {
    searchValue.value = value
  }

  defineExpose({
    getTreeData,
    treeData,
    checkedItems,
    setSearchValue,
    clearBread
  })
</script>

<style lang="less" scoped>
  @import '@/styles/mixins.less';

  .main-box {
    display: flex;
    overflow: hidden;
    width: 100%;
    height: 400px;
    border: 1px solid @border2;
    border-radius: 6px;

    .main-box__item {
      width: 100%;
      height: 100%;

      .ant-form-item {
        margin-bottom: 0;
      }

      .item__header {
        display: flex;
        justify-content: space-between;
        padding: 12px 14px 0;
        width: 100%;
        height: 44px;
        font-size: 14px;
        color: rgba(16, 16, 20, 0.95);
        background: #fafafa;
        font-weight: 700;
        border-bottom: 1px solid @border1;

        > div {
          &:last-child {
            color: rgba(21, 23, 27, 0.95);
            font-weight: 400;
            cursor: pointer;
          }
        }
      }

      .item__body {
        // overflow-y: auto;
        // padding: 8px 0 8px 12px;
        width: 100%;
        height: calc(398px - 36px);

        .body__search {
          padding: 8px 12px 0;
        }

        .crumbs {
          padding: 0 12px 12px;
        }

        .tree-body {
          overflow-y: auto;
          height: 300px;

          :deep(.ant-tree-title) {
            display: block;

            .elliptical-1();
          }
        }

        .select-item:has(.ant-radio-wrapper-checked) {
          background-color: rgba(17, 103, 240, 0.08);
        }

        .select-item-list {
          padding: 0 12px;
          width: 100%;

          .select-item {
            display: flex;
            justify-content: space-between;
            padding: 6px 4px;
            width: 100%;
            color: @text01;

            .select-item__name {
              width: 410px;
              color: @text01;
              .elliptical-1();
            }

            & + .select-item {
              margin-top: 10px;
            }
          }
        }
      }
    }
  }

  .disabled-color {
    color: rgba(0, 0, 0, 0.25) !important;
  }

  .bread-text {
    display: table-cell;
    max-width: 200px;
  }

  .tree-node__title {
    .elliptical-1();
  }
</style>
